package com.vdimitr.happycube.puzzle.piece.construction;

import com.vdimitr.happycube.puzzle.matrix.PuzzlePieceMatrixTransformer;
import com.vdimitr.happycube.puzzle.piece.PuzzlePiece;
import com.vdimitr.happycube.puzzle.piece.PuzzlePieceRotation;

/**
 * This class is responsible for rotating (clockwise and counter clockwise) and
 * flipping a puzzle piece as well as adjusting its matrix representation to
 * match the new state.
 * 
 * @see PuzzlePieceMatrixTransformer
 * 
 * @author vasileios.dimitriadis
 * 
 */
public class PuzzlePieceTransformer {

	/**
	 * Adjusts the matrix representation of a puzzle piece to match its state
	 */
	private PuzzlePieceMatrixTransformer matrixTransformer = new PuzzlePieceMatrixTransformer();

	/**
	 * Creates a new puzzle piece by rotating clockwise an existing one
	 * 
	 * @param puzzlePiece
	 *            the puzzle piece to rotate
	 * @param rotation
	 *            how many times to rotate the puzzle piece
	 * @return a puzzle piece copy of the input puzzle piece rotated and
	 *         adjusted
	 */
	public PuzzlePiece rotateClockwise(PuzzlePiece puzzlePiece,
			PuzzlePieceRotation rotation) {
		return rotateClockwise(puzzlePiece, rotation.getRotationCount());
	}

	/**
	 * Creates a new puzzle piece by rotating clockwise an existing one
	 * 
	 * @param puzzlePiece
	 *            the puzzle piece to rotate
	 * @param rotationCount
	 *            how many times to rotate the puzzle piece
	 * @return a puzzle piece copy of the input puzzle piece rotated and
	 *         adjusted
	 */
	public PuzzlePiece rotateClockwise(PuzzlePiece puzzlePiece,
			int rotationCount) {
		PuzzlePiece rotatedClockWisePuzzlePiece = new PuzzlePiece(puzzlePiece);
		for (int count = 1; count <= rotationCount; count++) {
			adjustRotationClockwise(rotatedClockWisePuzzlePiece);
			adjustMatrixClockwise(rotatedClockWisePuzzlePiece);
		}
		return rotatedClockWisePuzzlePiece;
	}

	/**
	 * Creates a new puzzle piece by rotating clockwise one time an existing one
	 * 
	 * @param puzzlePiece
	 *            the puzzle piece to rotate
	 * @return a puzzle piece copy of the input puzzle piece rotated and
	 *         adjusted
	 */
	public PuzzlePiece rotateClockwiseOnce(PuzzlePiece puzzlePiece) {
		return rotateClockwise(puzzlePiece, PuzzlePieceRotation.ONCE);
	}

	/**
	 * Increase rotation clockwise
	 * 
	 * @param puzzlePiece
	 *            the puzzle piece to rotate
	 */
	private void adjustRotationClockwise(PuzzlePiece puzzlePiece) {
		PuzzlePieceRotation rotation = puzzlePiece.getRotation();
		puzzlePiece.setRotation(rotation.next());
	}

	/**
	 * Rotate puzzle piece matrix representation clockwise
	 * 
	 * @param puzzlePiece
	 *            the puzzle piece to rotate
	 */
	private void adjustMatrixClockwise(PuzzlePiece puzzlePiece) {
		matrixTransformer.rotatePuzzlePieceMatrixClockwiseOnce(puzzlePiece);
	}

	/**
	 * Creates a new puzzle piece by rotating counter clockwise an existing one
	 * 
	 * @param puzzlePiece
	 *            the puzzle piece to rotate
	 * @param rotation
	 *            how many times to rotate the puzzle piece
	 * @return a puzzle piece copy of the input puzzle piece rotated and
	 *         adjusted
	 */
	public PuzzlePiece rotateCounterClockwise(PuzzlePiece puzzlePiece,
			PuzzlePieceRotation rotation) {
		return rotateCounterClockwise(puzzlePiece, rotation.getRotationCount());
	}

	/**
	 * Creates a new puzzle piece by rotating counter clockwise an existing one
	 * 
	 * @param puzzlePiece
	 *            the puzzle piece to rotate
	 * @param rotationCount
	 *            how many times to rotate the puzzle piece
	 * @return a puzzle piece copy of the input puzzle piece rotated and
	 *         adjusted
	 */
	public PuzzlePiece rotateCounterClockwise(PuzzlePiece puzzlePiece,
			int rotationCount) {
		PuzzlePiece rotatedCounterClockWisePuzzlePiece = new PuzzlePiece(
				puzzlePiece);
		for (int count = 1; count <= rotationCount; count++) {
			adjustRotationCounterClockwise(rotatedCounterClockWisePuzzlePiece);
			adjustMatrixCounterClockwise(rotatedCounterClockWisePuzzlePiece);
		}
		return rotatedCounterClockWisePuzzlePiece;
	}

	/**
	 * Creates a new puzzle piece by rotating counter clockwise one time an
	 * existing one
	 * 
	 * @param puzzlePiece
	 *            the puzzle piece to rotate
	 * @return a puzzle piece copy of the input puzzle piece rotated and
	 *         adjusted
	 */
	public PuzzlePiece rotateCounterClockwiseOnce(PuzzlePiece puzzlePiece) {
		return rotateCounterClockwise(puzzlePiece, PuzzlePieceRotation.ONCE);
	}

	/**
	 * Decrease rotation counter clockwise
	 * 
	 * @param puzzlePiece
	 *            the puzzle piece to rotate
	 */
	private void adjustRotationCounterClockwise(PuzzlePiece puzzlePiece) {
		matrixTransformer
				.rotatePuzzlePieceMatrixCounterClockwiseOnce(puzzlePiece);
	}

	/**
	 * Rotate puzzle piece matrix representation counter clockwise
	 * 
	 * @param puzzlePiece
	 *            the puzzle piece to rotate
	 */
	private void adjustMatrixCounterClockwise(PuzzlePiece puzzlePiece) {
		PuzzlePieceRotation rotation = puzzlePiece.getRotation();
		puzzlePiece.setRotation(rotation.previous());
	}

}
